/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_x86_shared_Encoding_x86_shared_h
#define jit_x86_shared_Encoding_x86_shared_h

#include "jit/x86-shared/Constants-x86-shared.h"

namespace js {
namespace jit {

namespace X86Encoding {

static const size_t MaxInstructionSize = 16;

// These enumerated values are following the Intel documentation Volume 2C [1],
// Appendix A.2 and Appendix A.3.
//
// Operand size/types as listed in the Appendix A.2.  Tables of the instructions
// and their operands can be found in the Appendix A.3.
//
// E = reg/mem
// G = reg (reg field of ModR/M)
// U = xmm (R/M field of ModR/M)
// V = xmm (reg field of ModR/M)
// W = xmm/mem64
// I = immediate
// O = offset
//
// b = byte (8-bit)
// w = word (16-bit)
// v = register size
// d = double (32-bit)
// dq = double-quad (128-bit) (xmm)
// ss = scalar float 32 (xmm)
// ps = packed float 32 (xmm)
// sd = scalar double (xmm)
// pd = packed double (xmm)
// z = 16/32/64-bit
// vqp = (*)
//
// (*) Some website [2] provides a convenient list of all instructions, but be
// aware that they do not follow the Intel documentation naming, as the
// following enumeration does. Do not use these names as a reference for adding
// new instructions.
//
// [1] http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-manual-325462.html
// [2] http://ref.x86asm.net/geek.html
//
// OPn_NAME_DstSrc
enum OneByteOpcodeID {
    OP_NOP_00                       = 0x00,
    OP_ADD_EbGb                     = 0x00,
    OP_ADD_EvGv                     = 0x01,
    OP_ADD_GvEv                     = 0x03,
    OP_ADD_EAXIv                    = 0x05,
    OP_OR_EbGb                      = 0x08,
    OP_OR_EvGv                      = 0x09,
    OP_OR_GvEv                      = 0x0B,
    OP_OR_EAXIv                     = 0x0D,
    OP_2BYTE_ESCAPE                 = 0x0F,
    OP_NOP_0F                       = 0x0F,
    OP_ADC_GvEv                     = 0x13,
    OP_SBB_GvEv                     = 0x1B,
    OP_NOP_1F                       = 0x1F,
    OP_AND_EbGb                     = 0x20,
    OP_AND_EvGv                     = 0x21,
    OP_AND_GvEv                     = 0x23,
    OP_AND_EAXIv                    = 0x25,
    OP_SUB_EbGb                     = 0x28,
    OP_SUB_EvGv                     = 0x29,
    OP_SUB_GvEv                     = 0x2B,
    OP_SUB_EAXIv                    = 0x2D,
    PRE_PREDICT_BRANCH_NOT_TAKEN    = 0x2E,
    OP_XOR_EbGb                     = 0x30,
    OP_XOR_EvGv                     = 0x31,
    OP_XOR_GvEv                     = 0x33,
    OP_XOR_EAXIv                    = 0x35,
    OP_CMP_EvGv                     = 0x39,
    OP_CMP_GvEv                     = 0x3B,
    OP_CMP_EAXIv                    = 0x3D,
#ifdef JS_CODEGEN_X64
    PRE_REX                         = 0x40,
#endif
    OP_NOP_40                       = 0x40,
    OP_NOP_44                       = 0x44,
    OP_PUSH_EAX                     = 0x50,
    OP_POP_EAX                      = 0x58,
#ifdef JS_CODEGEN_X86
    OP_PUSHA                        = 0x60,
    OP_POPA                         = 0x61,
#endif
#ifdef JS_CODEGEN_X64
    OP_MOVSXD_GvEv                  = 0x63,
#endif
    PRE_OPERAND_SIZE                = 0x66,
    PRE_SSE_66                      = 0x66,
    OP_NOP_66                       = 0x66,
    OP_PUSH_Iz                      = 0x68,
    OP_IMUL_GvEvIz                  = 0x69,
    OP_PUSH_Ib                      = 0x6a,
    OP_IMUL_GvEvIb                  = 0x6b,
    OP_JCC_rel8                     = 0x70,
    OP_GROUP1_EbIb                  = 0x80,
    OP_NOP_80                       = 0x80,
    OP_GROUP1_EvIz                  = 0x81,
    OP_GROUP1_EvIb                  = 0x83,
    OP_TEST_EbGb                    = 0x84,
    OP_NOP_84                       = 0x84,
    OP_TEST_EvGv                    = 0x85,
    OP_XCHG_GbEb                    = 0x86,
    OP_XCHG_GvEv                    = 0x87,
    OP_MOV_EbGv                     = 0x88,
    OP_MOV_EvGv                     = 0x89,
    OP_MOV_GvEb                     = 0x8A,
    OP_MOV_GvEv                     = 0x8B,
    OP_LEA                          = 0x8D,
    OP_GROUP1A_Ev                   = 0x8F,
    OP_NOP                          = 0x90,
    OP_PUSHFLAGS                    = 0x9C,
    OP_POPFLAGS                     = 0x9D,
    OP_CDQ                          = 0x99,
    OP_MOV_EAXOv                    = 0xA1,
    OP_MOV_OvEAX                    = 0xA3,
    OP_TEST_EAXIb                   = 0xA8,
    OP_TEST_EAXIv                   = 0xA9,
    OP_MOV_EbIb                     = 0xB0,
    OP_MOV_EAXIv                    = 0xB8,
    OP_GROUP2_EvIb                  = 0xC1,
    OP_ADDP_ST0_ST1                 = 0xC1,
    OP_RET_Iz                       = 0xC2,
    PRE_VEX_C4                      = 0xC4,
    PRE_VEX_C5                      = 0xC5,
    OP_RET                          = 0xC3,
    OP_GROUP11_EvIb                 = 0xC6,
    OP_GROUP11_EvIz                 = 0xC7,
    OP_INT3                         = 0xCC,
    OP_GROUP2_Ev1                   = 0xD1,
    OP_GROUP2_EvCL                  = 0xD3,
    OP_FPU6                         = 0xDD,
    OP_FPU6_F32                     = 0xD9,
    OP_FPU6_ADDP                    = 0xDE,
    OP_FILD                         = 0xDF,
    OP_CALL_rel32                   = 0xE8,
    OP_JMP_rel32                    = 0xE9,
    OP_JMP_rel8                     = 0xEB,
    PRE_LOCK                        = 0xF0,
    PRE_SSE_F2                      = 0xF2,
    PRE_SSE_F3                      = 0xF3,
    OP_HLT                          = 0xF4,
    OP_GROUP3_EbIb                  = 0xF6,
    OP_GROUP3_Ev                    = 0xF7,
    OP_GROUP3_EvIz                  = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
    OP_GROUP5_Ev                    = 0xFF
};

enum class ShiftID {
    vpsrlx = 2,
    vpsrldq = 3,
    vpsrad = 4,
    vpsllx = 6
};

enum TwoByteOpcodeID {
    OP2_UD2             = 0x0B,
    OP2_MOVSD_VsdWsd    = 0x10,
    OP2_MOVPS_VpsWps    = 0x10,
    OP2_MOVSD_WsdVsd    = 0x11,
    OP2_MOVPS_WpsVps    = 0x11,
    OP2_MOVDDUP_VqWq    = 0x12,
    OP2_MOVHLPS_VqUq    = 0x12,
    OP2_MOVSLDUP_VpsWps = 0x12,
    OP2_UNPCKLPS_VsdWsd = 0x14,
    OP2_UNPCKHPS_VsdWsd = 0x15,
    OP2_MOVLHPS_VqUq    = 0x16,
    OP2_MOVSHDUP_VpsWps = 0x16,
    OP2_MOVAPD_VsdWsd   = 0x28,
    OP2_MOVAPS_VsdWsd   = 0x28,
    OP2_MOVAPS_WsdVsd   = 0x29,
    OP2_CVTSI2SD_VsdEd  = 0x2A,
    OP2_CVTTSD2SI_GdWsd = 0x2C,
    OP2_UCOMISD_VsdWsd  = 0x2E,
    OP2_CMOVZ_GvEv      = 0x44,
    OP2_MOVMSKPD_EdVd   = 0x50,
    OP2_ANDPS_VpsWps    = 0x54,
    OP2_ANDNPS_VpsWps   = 0x55,
    OP2_ORPS_VpsWps     = 0x56,
    OP2_XORPS_VpsWps    = 0x57,
    OP2_ADDSD_VsdWsd    = 0x58,
    OP2_ADDPS_VpsWps    = 0x58,
    OP2_MULSD_VsdWsd    = 0x59,
    OP2_MULPS_VpsWps    = 0x59,
    OP2_CVTSS2SD_VsdEd  = 0x5A,
    OP2_CVTSD2SS_VsdEd  = 0x5A,
    OP2_CVTTPS2DQ_VdqWps = 0x5B,
    OP2_CVTDQ2PS_VpsWdq = 0x5B,
    OP2_SUBSD_VsdWsd    = 0x5C,
    OP2_SUBPS_VpsWps    = 0x5C,
    OP2_MINSD_VsdWsd    = 0x5D,
    OP2_MINSS_VssWss    = 0x5D,
    OP2_MINPS_VpsWps    = 0x5D,
    OP2_DIVSD_VsdWsd    = 0x5E,
    OP2_DIVPS_VpsWps    = 0x5E,
    OP2_MAXSD_VsdWsd    = 0x5F,
    OP2_MAXSS_VssWss    = 0x5F,
    OP2_MAXPS_VpsWps    = 0x5F,
    OP2_SQRTSD_VsdWsd   = 0x51,
    OP2_SQRTSS_VssWss   = 0x51,
    OP2_SQRTPS_VpsWps   = 0x51,
    OP2_RSQRTPS_VpsWps  = 0x52,
    OP2_RCPPS_VpsWps    = 0x53,
    OP2_ANDPD_VpdWpd    = 0x54,
    OP2_ORPD_VpdWpd     = 0x56,
    OP2_XORPD_VpdWpd    = 0x57,
    OP2_PUNPCKLDQ       = 0x62,
    OP2_PCMPGTB_VdqWdq  = 0x64,
    OP2_PCMPGTW_VdqWdq  = 0x65,
    OP2_PCMPGTD_VdqWdq  = 0x66,
    OP2_MOVD_VdEd       = 0x6E,
    OP2_MOVDQ_VsdWsd    = 0x6F,
    OP2_MOVDQ_VdqWdq    = 0x6F,
    OP2_PSHUFD_VdqWdqIb = 0x70,
    OP2_PSHUFLW_VdqWdqIb = 0x70,
    OP2_PSHUFHW_VdqWdqIb = 0x70,
    OP2_PSLLW_UdqIb     = 0x71,
    OP2_PSRAW_UdqIb     = 0x71,
    OP2_PSRLW_UdqIb     = 0x71,
    OP2_PSLLD_UdqIb     = 0x72,
    OP2_PSRAD_UdqIb     = 0x72,
    OP2_PSRLD_UdqIb     = 0x72,
    OP2_PSRLDQ_Vd       = 0x73,
    OP2_PCMPEQB_VdqWdq  = 0x74,
    OP2_PCMPEQW_VdqWdq  = 0x75,
    OP2_PCMPEQD_VdqWdq  = 0x76,
    OP2_HADDPD          = 0x7C,
    OP2_MOVD_EdVd       = 0x7E,
    OP2_MOVQ_VdWd       = 0x7E,
    OP2_MOVDQ_WdqVdq    = 0x7F,
    OP2_JCC_rel32       = 0x80,
    OP_SETCC            = 0x90,
    OP2_SHLD            = 0xA4,
    OP2_SHLD_GvEv       = 0xA5,
    OP2_SHRD            = 0xAC,
    OP2_SHRD_GvEv       = 0xAD,
    OP_FENCE            = 0xAE,
    OP2_IMUL_GvEv       = 0xAF,
    OP2_CMPXCHG_GvEb    = 0xB0,
    OP2_CMPXCHG_GvEw    = 0xB1,
    OP2_POPCNT_GvEv     = 0xB8,
    OP2_BSF_GvEv        = 0xBC,
    OP2_BSR_GvEv        = 0xBD,
    OP2_MOVSX_GvEb      = 0xBE,
    OP2_MOVSX_GvEw      = 0xBF,
    OP2_MOVZX_GvEb      = 0xB6,
    OP2_MOVZX_GvEw      = 0xB7,
    OP2_XADD_EbGb       = 0xC0,
    OP2_XADD_EvGv       = 0xC1,
    OP2_CMPPS_VpsWps    = 0xC2,
    OP2_PINSRW          = 0xC4,
    OP2_PEXTRW_GdUdIb   = 0xC5,
    OP2_SHUFPS_VpsWpsIb = 0xC6,
    OP2_PSRLW_VdqWdq    = 0xD1,
    OP2_PSRLD_VdqWdq    = 0xD2,
    OP2_PMULLW_VdqWdq   = 0xD5,
    OP2_MOVQ_WdVd       = 0xD6,
    OP2_PSUBUSB_VdqWdq  = 0xD8,
    OP2_PSUBUSW_VdqWdq  = 0xD9,
    OP2_PANDDQ_VdqWdq   = 0xDB,
    OP2_PADDUSB_VdqWdq  = 0xDC,
    OP2_PADDUSW_VdqWdq  = 0xDD,
    OP2_PANDNDQ_VdqWdq  = 0xDF,
    OP2_PSRAW_VdqWdq    = 0xE1,
    OP2_PSRAD_VdqWdq    = 0xE2,
    OP2_PSUBSB_VdqWdq   = 0xE8,
    OP2_PSUBSW_VdqWdq   = 0xE9,
    OP2_PORDQ_VdqWdq    = 0xEB,
    OP2_PADDSB_VdqWdq   = 0xEC,
    OP2_PADDSW_VdqWdq   = 0xED,
    OP2_PXORDQ_VdqWdq   = 0xEF,
    OP2_PSLLW_VdqWdq    = 0xF1,
    OP2_PSLLD_VdqWdq    = 0xF2,
    OP2_PMULUDQ_VdqWdq  = 0xF4,
    OP2_PSUBB_VdqWdq    = 0xF8,
    OP2_PSUBW_VdqWdq    = 0xF9,
    OP2_PSUBD_VdqWdq    = 0xFA,
    OP2_PADDB_VdqWdq    = 0xFC,
    OP2_PADDW_VdqWdq    = 0xFD,
    OP2_PADDD_VdqWdq    = 0xFE
};

enum ThreeByteOpcodeID {
    OP3_PSHUFB_VdqWdq   = 0x00,
    OP3_ROUNDSS_VsdWsd  = 0x0A,
    OP3_ROUNDSD_VsdWsd  = 0x0B,
    OP3_BLENDVPS_VdqWdq = 0x14,
    OP3_PEXTRB_EdVdqIb  = 0x14,
    OP3_PEXTRD_EdVdqIb  = 0x16,
    OP3_BLENDPS_VpsWpsIb = 0x0C,
    OP3_PTEST_VdVd      = 0x17,
    OP3_PINSRB_VdqEdIb  = 0x20,
    OP3_INSERTPS_VpsUps = 0x21,
    OP3_PINSRD_VdqEdIb  = 0x22,
    OP3_PMULLD_VdqWdq   = 0x40,
    OP3_VBLENDVPS_VdqWdq = 0x4A
};

// Test whether the given opcode should be printed with its operands reversed.
inline bool IsXMMReversedOperands(TwoByteOpcodeID opcode)
{
    switch (opcode) {
      case OP2_MOVSD_WsdVsd: // also OP2_MOVPS_WpsVps
      case OP2_MOVAPS_WsdVsd:
      case OP2_MOVDQ_WdqVdq:
      case OP3_PEXTRD_EdVdqIb:
        return true;
      default:
        break;
    }
    return false;
}

enum ThreeByteEscape {
    ESCAPE_38     = 0x38,
    ESCAPE_3A     = 0x3A
};

enum VexOperandType {
    VEX_PS = 0,
    VEX_PD = 1,
    VEX_SS = 2,
    VEX_SD = 3
};

inline OneByteOpcodeID jccRel8(Condition cond)
{
    return OneByteOpcodeID(OP_JCC_rel8 + cond);
}
inline TwoByteOpcodeID jccRel32(Condition cond)
{
    return TwoByteOpcodeID(OP2_JCC_rel32 + cond);
}
inline TwoByteOpcodeID setccOpcode(Condition cond)
{
    return TwoByteOpcodeID(OP_SETCC + cond);
}

enum GroupOpcodeID {
    GROUP1_OP_ADD = 0,
    GROUP1_OP_OR  = 1,
    GROUP1_OP_ADC = 2,
    GROUP1_OP_SBB = 3,
    GROUP1_OP_AND = 4,
    GROUP1_OP_SUB = 5,
    GROUP1_OP_XOR = 6,
    GROUP1_OP_CMP = 7,

    GROUP1A_OP_POP = 0,

    GROUP2_OP_ROL = 0,
    GROUP2_OP_ROR = 1,
    GROUP2_OP_SHL = 4,
    GROUP2_OP_SHR = 5,
    GROUP2_OP_SAR = 7,

    GROUP3_OP_TEST = 0,
    GROUP3_OP_NOT  = 2,
    GROUP3_OP_NEG  = 3,
    GROUP3_OP_MUL  = 4,
    GROUP3_OP_IMUL = 5,
    GROUP3_OP_DIV  = 6,
    GROUP3_OP_IDIV = 7,

    GROUP5_OP_INC   = 0,
    GROUP5_OP_DEC   = 1,
    GROUP5_OP_CALLN = 2,
    GROUP5_OP_JMPN  = 4,
    GROUP5_OP_PUSH  = 6,

    FILD_OP_64      = 5,

    FPU6_OP_FLD     = 0,
    FPU6_OP_FISTTP  = 1,
    FPU6_OP_FSTP    = 3,
    FPU6_OP_FLDCW   = 5,
    FPU6_OP_FISTP   = 7,

    GROUP11_MOV = 0
};

static const RegisterID noBase = rbp;
static const RegisterID hasSib = rsp;
static const RegisterID noIndex = rsp;
#ifdef JS_CODEGEN_X64
static const RegisterID noBase2 = r13;
static const RegisterID hasSib2 = r12;
#endif

enum ModRmMode {
    ModRmMemoryNoDisp,
    ModRmMemoryDisp8,
    ModRmMemoryDisp32,
    ModRmRegister
};

} // namespace X86Encoding

} // namespace jit
} // namespace js

#endif /* jit_x86_shared_Encoding_x86_shared_h */
